Skip to content

FullStackHero 10 .NET Starter Kit Release Merge#1152

Merged
iammukeshm merged 664 commits into
mainfrom
develop
May 27, 2026
Merged

FullStackHero 10 .NET Starter Kit Release Merge#1152
iammukeshm merged 664 commits into
mainfrom
develop

Conversation

@iammukeshm
Copy link
Copy Markdown
Member

#Architecture

  • Modular monolith with modules for Identity, Multitenancy, Auditing; mediator-based CQRS; background jobs; caching; mailing; storage abstraction.
  • Minimal API host with Identity (JWT, refresh, roles/permissions), Multitenancy (Finbuckle, provisioning lifecycle), Auditing (request/response/security/exception with background sink).
  • Shadcn-inspired MudBlazor wrappers; Dashboard/Profile/Audits pages wired to generated API clients; BFF-style auth delegating handler; theme/layout shell.
  • NSwag config + script to regenerate clients (scripts/openapi/generate-api-clients.ps1 -SpecUrl "<spec>"); Blazor consumes generated clients.
  • Multi-app AWS scaffolding (API/Blazor) with modular structure using Terraform.
  • Mediator Handlers and Validation
  • RateLimiting / Storage / Outbox Pattern

@iammukeshm iammukeshm requested a review from Copilot December 9, 2025 09:12
@iammukeshm iammukeshm self-assigned this Dec 9, 2025
@iammukeshm iammukeshm added the enhancement New feature or request label Dec 9, 2025
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the FullStackHero 10 .NET Starter Kit, implementing a modular monolith architecture with comprehensive modules for Identity, Multitenancy, and Auditing. The implementation includes a mediator-based CQRS pattern, JWT authentication with refresh tokens, role/permission-based authorization, background job support, caching abstractions, mailing services, and storage abstractions (local and S3). The Blazor client uses Shadcn-inspired MudBlazor wrappers with generated API clients via NSwag, while the infrastructure includes multi-app AWS scaffolding using Terraform and OpenTelemetry-based observability.

Key Changes

  • Modular architecture with separate Identity, Multitenancy, and Auditing modules implementing contracts and handlers
  • JWT authentication, role/permission system, and Finbuckle multitenancy with per-tenant provisioning lifecycle
  • Auditing pipeline with request/response/security/exception tracking and background sink for SQL persistence
  • OpenTelemetry integration, rate limiting, storage abstraction (local/S3), and comprehensive building blocks for caching, jobs, mailing, and persistence

Reviewed changes

Copilot reviewed 295 out of 1048 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Directory.Packages.props Updated package versions to .NET 10.0 and newer dependencies including Finbuckle 10.0.0, Mediator 3.1.0-preview.14, Hangfire 1.8.22, and OpenTelemetry 1.14.0
Directory.Build.props Enhanced with .NET 10.0 target, comprehensive code analysis settings, NuGet metadata, and stricter quality controls
BuildingBlocks/Web/*.cs New Web building block with OpenAPI/Scalar integration, OpenTelemetry, Serilog logging, rate limiting, security headers, CORS, versioning, and module loading
BuildingBlocks/Storage/*.cs Storage abstraction supporting local filesystem and AWS S3 with file type validation and upload/removal operations
BuildingBlocks/Shared/*.cs Shared contracts for multitenancy (AppTenantInfo), identity (claims, permissions, roles), pagination, and database options
BuildingBlocks/Persistence/*.cs Persistence infrastructure with specifications pattern, EF Core extensions, and database initialization interfaces
Modules/Identity/Modules.Identity.Contracts/*.cs Identity module contracts including commands/queries for token generation, user management, role management, and associated DTOs
Modules/Auditing/Modules.Auditing.Contracts/*.cs Auditing contracts with event types, payloads, DTOs, and interfaces for audit publishing, serialization, and sinking
Modules/Auditing/Modules.Auditing/*.cs Auditing implementation with SQL sink, EF interceptor, HTTP middleware for request/response capture, channel-based publisher, and query handlers

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@iammukeshm iammukeshm marked this pull request as draft December 9, 2025 09:13
@maxiar
Copy link
Copy Markdown
Contributor

maxiar commented Dec 10, 2025

#Architecture

  • Modular monolith with modules for Identity, Multitenancy, Auditing; mediator-based CQRS; background jobs; caching; mailing; storage abstraction.
  • Minimal API host with Identity (JWT, refresh, roles/permissions), Multitenancy (Finbuckle, provisioning lifecycle), Auditing (request/response/security/exception with background sink).
  • Shadcn-inspired MudBlazor wrappers; Dashboard/Profile/Audits pages wired to generated API clients; BFF-style auth delegating handler; theme/layout shell.
  • NSwag config + script to regenerate clients (scripts/openapi/generate-api-clients.ps1 -SpecUrl "<spec>"); Blazor consumes generated clients.
  • Multi-app AWS scaffolding (API/Blazor) with modular structure using Terraform.
  • Mediator Handlers and Validation
  • RateLimiting / Storage / Outbox Pattern

Wow! You woke up!! :) Excelent works, I going to clone and test it... Please check, you forgot push the /docs folders because is added in .gitignore: "/docs"
BTW.... What IDE or stack you use or recommend to work better, get a good experience with this starter kit, may be VS 2026 + Copilot, or VS Code + Codex or Cursor + Another IA Model, what is your experience creating this template, Did you use any AI Assistance with some .MD spec files to define the software architect guidelines or something like that?

Thanks in advanced.

@iammukeshm
Copy link
Copy Markdown
Member Author

@maxiar

I am currently using VS2026.
Docs is ignored purposely, as they will be on another repo. It's still a WIP.
For AI Code Guidance, currently testing with Codex CLI. Trying to formulate a framework for a nice workflow experience. Will write about it on my blog once I figure it out.

@maxiar
Copy link
Copy Markdown
Contributor

maxiar commented Dec 12, 2025

@maxiar

I am currently using VS2026.

Docs is ignored purposely, as they will be on another repo. It's still a WIP.

For AI Code Guidance, currently testing with Codex CLI. Trying to formulate a framework for a nice workflow experience. Will write about it on my blog once I figure it out.

Perfect approach, check that may be some ideas are usefull:

https://medium.com/@mikhail.petrusheuski/steal-these-25-prompts-the-rules-workflows-that-made-our-net-team-faster-27899ece4dcc

And this "spec driven AI design":

https://medium.com/@mikhail.petrusheuski/steal-these-25-prompts-the-rules-workflows-that-made-our-net-team-faster-27899ece4dcc

@iammukeshm
Copy link
Copy Markdown
Member Author

iammukeshm commented Dec 12, 2025

@maxiar looks like its a member only story. any crucial takeaways?

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread src/BuildingBlocks/Web/Exceptions/GlobalExceptionHandler.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/ThemeStateFactory.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/ThemeStateFactory.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/ThemeStateFactory.cs Fixed
Comment thread src/Modules/Identity/Modules.Identity/Services/TokenService.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Modules/Identity/Modules.Identity/Services/TokenService.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Mar 26, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
fullstackhero 4b7a641 May 24 2026, 08:07 AM

Comment thread src/BuildingBlocks/Caching/DistributedCacheService.cs Fixed
Comment thread src/BuildingBlocks/Caching/DistributedCacheService.cs Fixed
Comment thread src/BuildingBlocks/Caching/HybridCacheService.cs Fixed
Comment thread src/BuildingBlocks/Caching/HybridCacheService.cs Fixed
Comment thread src/BuildingBlocks/Web/Idempotency/IdempotencyEndpointFilter.cs Fixed
Comment thread src/BuildingBlocks/Web/Idempotency/IdempotencyEndpointFilter.cs Fixed
Comment thread src/Playground/Playground.Blazor/Services/SimpleBffAuth.cs Fixed
Comment thread src/Host/FSH.Starter.Api/DevSeeding/DevDataSeeder.cs Fixed
iammukeshm and others added 14 commits May 13, 2026 11:38
30 tests covering DM DirectKey ordering, group-DM creator-as-admin,
member add/remove rules, MarkRead watermark, slug normalization,
message edit/delete author + moderator gates, reply-count bumps,
and tombstone idempotency.

Exposes Modules.Chat internals to Chat.Tests + Integration.Tests
so internal helpers (IncrementReplyCount/DecrementReplyCount) can
be exercised directly in unit tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ChatChannelsTests — happy paths, archive/restore roundtrip,
find-or-create DM idempotency via DirectKey, add/remove member,
auth gates, validation of self-DM rejection.

ChatMessagesTests — send/edit/delete lifecycle, cursor pagination,
thread replies + parent ReplyCount bump, top-level filter excludes
thread replies, mark-read watermark drops UnreadCount to zero,
1-level thread depth enforcement, 404s and 400 for missing/invalid.

Also corrects an outdated comment in ChatModule.cs — ListMyChannels
maps GET /channels (not /channels/me).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a single shared AppHub at /api/v1/realtime/hub (BuildingBlocks/Web/Realtime)
with the Redis backplane wired when CachingOptions:Redis is set. The hub joins
each connection to user:{userId} and to channel:{channelId} for every channel the
user is a member of (looked up via the new IUserChannelLookup that the Chat module
implements against ChatDbContext).

Chat handlers now publish realtime events after SaveChanges:
  SendMessage   → ChatMessageCreated         → channel:{id}
  EditMessage   → ChatMessageEdited          → channel:{id}
  DeleteMessage → ChatMessageDeleted         → channel:{id}
  AddMembers    → ChatChannelMemberAdded     → channel:{id}
                + ChatChannelAdded            → user:{newMember}
  RemoveMember  → ChatChannelMemberRemoved   → channel:{id}
                + ChatChannelRemoved          → user:{removed}
  MarkRead      → ChatChannelRead             → user:{caller} (cross-tab badge clear)

Also extends the JWT bearer OnMessageReceived path allow-list to accept
?access_token= for the hub path — browsers can't send Authorization headers on
WebSocket upgrade or EventSource fallback. Path-scoped to keep the exemption
narrow.

Existing 23 chat integration tests still pass; SignalR integration tests added
in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tests via in-memory HubConnection (long-polling transport since the
TestServer has no WebSocket support):
- ChatMessageCreated lands on the channel group when a message is sent
- ChatMessageEdited mirrors edits across the channel
- ChatMessageDeleted mirrors deletes across the channel
- ChatChannelRead lands on the user's own group (cross-tab badge clear)
- Hub rejects unauthenticated connections

Also fixes AppHub authentication: ICurrentUser depends on IHttpContextAccessor,
which doesn't pin the originating negotiate HttpContext to subsequent hub
invocations. Switched to reading the user id directly off Context.User
(NameIdentifier / sub / uid) which is the canonical SignalR pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gration

Peer module to Chat. Module Order 750 runs ahead of Chat (800) so its
integration-event subscribers are registered before Chat publishes —
handler registration is order-sensitive in the in-memory event bus.

- Modules.Notifications + .Contracts projects, slnx + Api + Migrations refs
- NotificationPermissions (Inbox.View / Inbox.MarkRead; both basic)
- Notification aggregate (UserId, Type, Title, Body, Link, Source,
  MetadataJson, ReadAtUtc, CreatedAtUtc) + MarkRead()
- NotificationsDbContext on schema "notifications" + EF config with the
  composite (UserId, ReadAtUtc, CreatedAtUtc) index that covers every
  inbox query path
- AddIntegrationEventHandlers wiring so Slice 3.5's mention handler
  picks up automatically once written
- InitialNotifications EF migration

Mediator markers intentionally omitted from Program.cs until Slice 3.3
introduces the first ICommand/IQuery (Mediator source-gen MSG0007).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Under /api/v1/notifications, all scoped to the authenticated caller:
  GET    /                   ListNotifications?unreadOnly=&page=&pageSize=
  GET    /unread-count       GetUnreadCount     — bell badge
  POST   /{id}/read          MarkNotificationRead — single
  POST   /read-all           MarkAllNotificationsRead — returns count updated

Mark-read commands use a (Id, UserId) filter so callers can only mutate
their own rows (404 instead of 403 on cross-user reach). MarkAll uses
ExecuteUpdateAsync for a single bulk UPDATE — no row materialization.

Also re-enables Notifications markers in Program.cs::AddMediator now
that ICommand/IQuery types exist (was deferred to avoid MSG0007).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a child MessageMention entity attached to Message.Mentions, with
position (StartIndex, Length) preserved from the raw body so the UI
can highlight without re-parsing.

SendMessageCommandHandler now:
- extracts @username tokens via MentionParser (Slack/Discord-style regex)
- resolves them via IMentionResolver (default: filters Identity user list)
- drops unresolved tokens and self-mentions silently
- attaches one MessageMention per resolved (other) user
- publishes one MentionedInChannelIntegrationEvent per distinct mentioned
  user (Notifications module subscribes in Slice 3.5)

MentionedInChannelIntegrationEvent lives in Modules.Chat.Contracts (the
public surface). MentionResolver implementation stays internal to Chat.

AddMessageMentions EF migration creates the chat.MessageMentions table
with cascade-delete FK to Messages and indexes on MentionedUserId +
MessageId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Notifications subscribes to the Chat module's MentionedInChannelIntegrationEvent:
- Persists a chat.mention Notification row for the mentioned user
- Pushes a NotificationCreated SignalR event to user:{id} so the bell
  badge updates live across that user's tabs

Handler lives in Notifications/IntegrationEventHandlers/ (top-level, not
under Features/) so arch tests don't flag the non-versioned folder
under Features — matches the Identity Events/ convention.

Integration tests cover the three meaningful paths:
- @-mention persists a Notification visible in the recipient's inbox
- @-mention fires NotificationCreated over SignalR (long-polling test
  transport; receives via HubConnection authenticated as the recipient)
- Self-mentions are dropped (no notification row written)

Tests bypass /register's email-confirmation gate via UserManager so the
second user can sign in immediately — matches the existing
EmailConfirmationTests pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Threads:
- GET /chat/messages/{id}/replies — cursor-paged, reverse chronological
  via Guid v7. RequireMember through parent's channel.
- DeleteMessage already decrements parent ReplyCount (Slice 1) — covered
  by a new integration test here.

Reactions:
- MessageReaction child entity attached to Message.Reactions; unique
  index on (MessageId, UserId, Emoji) so a user can react with the
  same emoji only once per message.
- AddReaction/RemoveReaction handlers — idempotent (re-add and absent-
  remove are silent no-ops, no spurious broadcasts).
- ChatReactionChanged SignalR event broadcast to channel:{id} on each
  mutation with { channelId, messageId, userId, emoji, kind }.
- MessageDto now carries Reactions (forces the dashboard contract to
  pick up the new shape).

8 new integration tests + 6 new unit tests; existing 23 chat REST +
5 realtime + 3 mention all still green.

AddMessageReactions EF migration creates chat.MessageReactions with
cascade FK to Messages and the composite unique index.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GET /api/v1/chat/search?q=&channelId=&page=&pageSize= — ranked by ts_rank
with a stable Id-desc tiebreaker for deterministic pagination.

- AddMessagesFullTextSearch migration: GENERATED ALWAYS AS tsvector
  column on chat.Messages + GIN index. Kept out of the EF model
  (raw SQL) since EF doesn't track GENERATED columns and we never
  write to it from C#.
- SearchMessagesQueryHandler issues a single parameterized
  FromSqlInterpolated query joining against the caller's member
  channels. Channel-scoping is enforced server-side so non-members
  cannot leak results from channels they don't belong to.
- Uses websearch_to_tsquery so callers can write natural search
  syntax (quoted phrases, OR, -exclude) without server-side parsing.
- Soft-deleted messages (DeletedAtUtc IS NOT NULL) are excluded.

4 integration tests cover: token match in member channel, channelId
narrowing, non-member exclusion (registered second user, blank result),
and soft-delete tombstone exclusion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two HubConnections sharing a channel:
- Typing broadcasts ChatTypingStarted to other channel members
- Rapid Typing calls within the 3s window throttle down to a single
  ChatTypingStarted event (distributed-cache marker)
- Non-members never see the typing broadcast

Adds an EventInbox.DrainAsync helper that collects every event within
a window — needed because the throttle test asserts a *count* of 1
rather than just "at least one matched".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OwnerType="ChatChannel", OwnerId=channelId. Dashboard's RequestUploadUrl
flow now authorizes attachments routed through chat:
- Attach + Read: caller must be a member of the channel (current
  membership, ASP.NET-Core-independent check against ChatDbContext)
- Delete: uploader-only. Moderators can already remove the whole
  message via Messages.DeleteAny; cascading delete on Message → Attachment
  takes care of the row.

3 integration tests verify the policy enforces the membership gate
on /api/v1/files/upload-url:
- Member sees 200 with a presigned PUT URL
- Non-member sees 403 ForbiddenException → ProblemDetails
- Missing ownerId on a ChatChannel attach also sees 403 — the policy
  refuses to attach files that aren't bound to a channel

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slack-style /chat experience built on the same editorial-console aesthetic
as the rest of the dashboard (Geist + JetBrains Mono, oklch surfaces,
brand-tinted atmospherics). Distinctive moves layered in:

- Channel rail: editorial sidebar with mono uppercase section captions,
  unread badges in primary tone, active row gets the dashboard's 2px
  brand bar.
- Channel header: atmospheric brand glow tucked in the trailing edge
  via .chat-channel-header pseudo-element.
- Message stream: virtualised via @tanstack/react-virtual; chapter-break
  day rules in mono uppercase ("TODAY" / "YESTERDAY" / weekday); message
  blocks merge for same-author sub-5min runs to reduce visual noise;
  caller's own messages get a 2px brand-tinted left edge; @mentions
  render as inline chat-mention pills.
- Reaction chips: outlined pill that lights up in primary tones for the
  caller's own reactions; quick-pick popover (👍 🎉 ❤️ 👀 🔥 🚀).
- Composer plinth: brand-tinted ring on focus, auto-grow textarea, send
  button springs to brand on hover, keyboard hint row in mono.
- Typing indicator: three-dot wave keyframe (fsh-chat-typing) with 160ms
  phase offset between dots; fires Typing(channelId) on each keystroke
  (hub itself rate-limits per project_signalr_hub_user gotcha).
- NotificationBell: bell with destructive badge count in topbar; opens a
  brand-glow dropdown inbox mirroring the profile dropdown's atmospheric
  hero panel; live-patches via NotificationCreated SignalR event.

Realtime infrastructure:
- RealtimeProvider opens a singleton @microsoft/signalr HubConnection to
  /api/v1/realtime/hub authenticated via accessTokenFactory (no manual
  query-string fiddling). Auto-reconnect with [2s, 5s, 10s, 30s] backoff
  + lazy rebuild on hard close so JWT rotation is picked up cleanly.
  useRealtimeEvent<T>("EventName", handler) typed hook with ref-based
  handler stability so subscribers don't re-bind on every render.
- Mounted alongside SseProvider in AppShell so the chat page and the
  notification bell share one connection.

Wires up: nav-data.ts /chat entry, /chat + /chat/:channelId routes,
NotificationBell mounted in the topbar before the profile dropdown.

Also lands the previously-uncommitted dashboard layout refactor in this
branch (sidebar + topbar + mobile-nav + app-shell + several page CSS
tweaks + eslint.config) since Mukesh approved touching them. Those
changes were authored across earlier turns and are not chat-specific —
the chat work just sits cleanly on top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, multi-line edit, delete confirm + 6 more

11-item audit-and-polish through the /chat surface and NotificationBell, holding
the editorial-console aesthetic (Geist + JetBrains Mono, oklch surfaces, 2px
brand bar, atmospheric radial glows). Entirely client-side — no API surface
changes.

Distinctive moves layered in:

- Unread divider: the .chat-unread-divider CSS was already defined but never
  rendered. Now wired in MessageList between the watermark message and the
  first unread, with the watermark snapshotted via a ref keyed on channelId
  so it stays put when mark-read fires.
- Jump-to-bottom pill: spring-in keyframe (fsh-chat-jump-in), brand-tinted
  ring + glow, mono caption, monospace tabular count chip. Only ticks the
  unseen counter when a message lands AND user is scrolled away from bottom.
- Channel search in the rail: mono-placeholder filter input (case-insensitive
  substring match against channelTitle), clearable via X chip on the right.
  Footer count flips to "N of M" while filtering.
- Live connection state: new RealtimeStatusPill component consumes
  useRealtime().status and renders LIVE/RECONNECTING/OFFLINE with a colored
  dot. Reconnecting dot pulses (fsh-chat-status-pulse). Mounted in both the
  rail footer (replacing static "N channels") and the bell dropdown footer
  (replacing static "Live · realtime"). Uses --color-success / --color-warning
  design tokens so it themes correctly.
- Elevated EmptyState: atmospheric brand-glow hero panel with dotted texture
  overlay, mono uppercase eyebrow caption, display-typography heading, and
  three kbd hint chips (↵ Send · ⇧↵ Newline · @ Mention) for affordance.
- Multi-line edit: replaced the single-line Input with an auto-growing
  textarea matching the composer (6-line cap, useLayoutEffect grow, caret
  parked at end on mount, ↵=save / ⇧↵=newline / Esc=cancel).
- Delete confirmation: routes the trash button through a Dialog with a
  brand-bordered blockquote preview of the message body (truncated at 140
  chars). Sonner toast on success / failure.
- Mention pills are now <button>: interactive, keyboard-focusable, hover
  state via button.chat-mention CSS rule, click copies "@username" to
  clipboard with a sonner toast.
- New DM affordance: Plus button on the Direct Messages section opens a
  NewDmDialog backed by searchUsers (250ms debounce, min 2 chars). Picker
  shows avatar + display name + email. Selection fires findOrCreateDm and
  navigates to the resulting channel.
- Honest composer hint: "Type @username to mention" replaces the misleading
  "@ Mention" copy (no autocomplete is shipped — literal @ typing still
  becomes a mention notification server-side).

Verification:
- npx tsc -b → 0 errors
- npx vite build → 0 errors, 2.92s. Chat ships as a 51.86 KB / 14.70 KB gzip
  chunk.
- npx eslint on chat / notifications / realtime → 0 errors, 0 new warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iammukeshm and others added 25 commits May 23, 2026 19:00
… audit

Findings and fixes from a race-condition / code-smell audit (Roslyn navigator
detect_antipatterns, find_dead_code, detect_circular_dependencies + targeted
concurrency sweeps). Concurrency hygiene was already strong — no async void,
no sync-over-async, no fire-and-forget, thread-safe singletons, zero circular
deps. The genuine items:

- Audit (static): swap the enricher list for an atomically-replaced immutable
  array read once per WriteAsync. Kills the latent "collection modified" race
  if Configure() ever runs concurrently with enrichment, and removes a
  process-global mutable-List test-isolation hazard.
- QuotaEnforcementMiddleware: inject TimeProvider instead of DateTimeOffset.UtcNow
  for Retry-After, matching the rest of the Quota subsystem (deterministic in tests).
- PresenceTracker.Connect: also report the offline->online transition when the
  AddOrUpdate update factory resurrects a count==0 key (Disconnect set 0 but
  hadn't removed it yet) — previously a reconnect in that window missed the
  presence broadcast.
- ChannelMember: remove dead SetMuted (zero callers); IsMuted is now get-only
  (still persisted + exposed via DTO, populated by EF via backing field).

Investigated but intentionally NOT changed (false positives confirmed):
- catch(Exception) in hosted services already filter OperationCanceledException.
- Flagged EF "missing AsNoTracking" queries all read-then-mutate-then-save
  (tracking required); the read-only paths already use AsNoTracking.
- find_dead_code hits for ChannelAuthorization / EntityEntryExtensions are
  used extension methods; EF model snapshots are tooling artifacts.

All unit (779) + integration (665) + middleware (5) tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (accurate to current codebase)

Establish AGENTS.md as the canonical, tool-neutral AI guide (CLAUDE.md/GEMINI.md
are thin @import bridges) and build out .agents/ guidance verified against the
current code via Roslyn navigation + real-file extraction.

Rules (.agents/rules/) — lean, on-demand, indexed from AGENTS.md:
- cross-cutting: architecture, api-conventions, database, eventing, caching, jobs,
  resilience, storage, security, realtime, logging, testing, integration-testing
- per-module: identity, multitenancy, chat, files, webhooks, auditing, billing,
  catalog, tickets, notifications
- frontend: shared + admin + dashboard (the two React apps' real divergences)
- replaces the old flat modules.md/persistence.md/testing-rules.md

Skills (.agents/skills/) — audited the 6 existing against the live codebase and
rewrote them (they were stale: IRepository<T>, PagedList<T>, Moq/FluentAssertions,
Guid.NewGuid, class-level [FshModule(Order=n)], DbContext vs BaseDbContext, only 2
of the 4 module-registration sites). Added 5: add-react-page, add-full-slice,
create-migration, add-integration-event, add-permission. Skills hold the recipe.

Workflows (.agents/workflows/) — reconciled so they orchestrate and delegate to
skills (no duplicated/contradictory templates); fixed stale facts (migrations run
via DbMigrator, not on startup; TenantDbContext not "MultitenancyDbContext").

Also corrects the [FshModule] attribute (assembly-level positional, not class-level)
in architecture.md and the AGENTS.md pointer.

Docs only; no source/build impact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop nswag.consolecore from dotnet-tools and delete scripts/openapi/*
  (NSwag-based C# client generation is no longer used).
- Remove scripts/test-cli.ps1 and the requirements/frontend-and-platform.md
  planning doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…page redesign, v10

- AI-Driven Development docs section (overview, AGENTS.md & .agents/, skills &
  workflows, developing with Claude Code) + section registration.
- Sponsorship: sticky sponsor card atop the docs ToC, and a "Back the build"
  home section (Open Collective), placed right under the architecture section.
- Homepage: new "Stack at a glance" bento opener; the architecture section
  rebuilt as a modular-monolith + vertical-slice diagram (two balanced boards);
  FSH brand mark in the hero; "Who it's for" redesigned as icon-led cards;
  module-card code panels removed; competitor product references removed.
- Version: product version aligned to v10 (.NET 10) across the homepage and
  docs copy. API version paths (/api/v1, Features/v1, Contracts.v1) untouched.
- Fix: type the header nav `match` field — astro check now 0 errors / 0 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lish

- Add "Tested to production standard" landing section (Testing.astro): a
  four-pillar scoreboard (unit / integration / E2E / architecture) over a
  green test-run terminal, leading with real-PostgreSQL-via-Testcontainers.
  Wire in as section 05; renumber WhoItsFor/FAQ/FinalCta to 06/07/08. Sync
  stale test counts in Hero (900+ -> 1,400+) and the FAQ answer.
- Theme toggle: replace the muddy CSS token cross-fade with a View Transitions
  API snapshot cross-fade (GPU-composited, text stays crisp, no frame drops);
  clean instant fallback for reduced-motion / unsupported browsers.
- Mobile responsiveness pass on the homepage: drop heading bases (sections
  30px, hero 34px) so they stop dominating phones; hero stats, TechStack, and
  the testing scoreboard go single-column on mobile; hero terminal scrolls
  instead of clipping; FAQ answers reclaim full width on mobile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t, React SPA hosting

- Pin Terraform >= 1.15.4 and AWS provider ~> 6.46 in the roots; permissive
  floors (>= 1.15 / >= 6.0) in child modules. Commit a multi-platform
  .terraform.lock.hcl locked to aws 6.46.0.
- Collapse the shared/ -> app_stack/ wrapper into a single root (provider +
  backend now live in app_stack/), removing ~500 lines of duplicated vars and
  outputs plus a passthrough bug that silently dropped ~30 tunables. Dedupe
  tags via provider default_tags.
- Add reusable modules/static_site (private S3 + OAC CloudFront, SPA
  403/404->index.html fallback, default-on managed security headers,
  Terraform-owned config.json) and host the two React SPAs (admin, dashboard)
  that replaced the removed server-rendered Blazor service. SPA CloudFront
  origins are auto-added to the API CORS allow-list. No Route53/domain
  resources (custom aliases optional).
- Drop the unnecessary api_task_secrets IAM policy (least privilege).
- Add one-command deploy.sh / deploy.ps1: terraform apply -> optional API
  image build/push -> build + s3 sync + CloudFront invalidate for both SPAs.
- Refresh README; terraform fmt + validate clean against aws 6.46.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…laude

Pre-existing working-tree changes outside the Terraform audit, committed so the
branch is clean:
- templates/ NuGet pack csproj, .template.config/template.json, README-template
- CLI NewCommand updates; AppHost + solution (slnx) wiring
- CI: add template-smoke workflow, ci.yml tweak
- docs: site.ts, breadcrumb helper, llms.txt/robots.txt, Cloudflare _headers/_redirects, error-handling page
- .gitignore: exclude local .claude worktrees, scheduled_tasks.lock, last30days.env

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ex pages "Overview"

- sidebar: detect the Astro 6 glob-loader bare-dir index id ('frontend' as well
  as 'frontend/index') so every section's overview is pulled out and rendered
  first, instead of leaking into the page list and sorting by order.
- breadcrumbs: a section overview now renders 'Docs / <Section>' (section is the
  current page) instead of duplicating it ('Docs / Modules / Modules'); fixed in
  both the visible component and the JSON-LD schema.
- name all 15 category index pages 'Overview' (with descriptive per-page seo.title).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…; rewrite install paths

- deployment/aspire: services that spin up locally + the design decisions.
- deployment/database-migrations: the DbMigrator (commands, multitenancy, prod usage).
- deployment/aws-terraform: end-to-end AWS deploy from prerequisites to one command.
- deployment/ci-cd: the GitHub Actions pipeline + template smoke test.
- getting-started/install: four install paths with screenshot placeholders.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delete the Blazor.UI building-block page and drop Blazor.UI / FSH.Starter.Blazor
from the project trees, the building-blocks list (llms.txt), and the CORS CSP
note. Competitor comparisons (BlazorPlate, ABP) and legacy blazor-* redirects
are kept intentionally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- deep-dive: add the subclass 'base.OnModelCreating LAST' rule, the documented
  Billing isolation exception (plain DbContext + manual TenantId), per-tenant
  migration ordering, and the AsyncLocal test gotcha.
- fix multitenancy module links that pointed at the section index instead of
  the deep-dive; cross-link Billing to the documented exception.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dingBlocks tests (#1244)

* fix(tests): make BuildingBlocks dependency checks cross-platform

ProjectReference Include paths are authored with Windows separators
(..\Core\Core.csproj). Path.GetFileNameWithoutExtension only treats
"\" as a separator on Windows, so on Linux CI it returned the full
path minus extension (..\Core\Core) instead of the bare name (Core).

This caused BuildingBlocks_Should_Follow_Layered_Dependencies to report
12 false-positive violations on Linux (passed locally on Windows), and
silently disabled the module-reference enforcement in
BuildingBlocks_Projects_Should_Not_Reference_Modules_Directly.

Normalize "\" to "/" before extracting the project name so both checks
work on every platform.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(webhooks): require http(s) scheme for subscription URL

Uri.TryCreate(UriKind.Absolute) accepts a leading-/ path as an implicit
file:// URI on Unix (but not Windows), so "/relative/path" passed
validation on the Linux CI runner while the test passed locally on
Windows. A webhook target must be an HTTP(S) endpoint regardless of
platform, so assert the scheme explicitly. Fixes the Linux-only failure
of Create_Should_Fail_When_Url_Relative.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Loads gtag.js site-wide via the base layout head. Both script tags
use is:inline so Astro emits them verbatim instead of bundling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore: extract docs site to a separate repo (fullstackhero/docs)

The Astro docs site now lives at github.com/fullstackhero/docs. Remove
docs/ from this repo and repoint the references that pointed into it.

- Move docs/superpowers/ (audits, specs, plans -- whole-project notes,
  not docs-site content) to repo root superpowers/, preserving history
- Remove the docs/ row from the AGENTS.md repo map
- Drop docs/{node_modules,dist,.astro} entries from .gitignore
- Repoint the README "deeper story" link to the new repo
- Drop "docs" from the code-reviewer workflow change-area grouping

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@
fix(docker): create MinIO bucket in prod compose + add migrator to root compose

deploy/docker (prod): nothing created the S3 bucket the Files module
writes to (S3StorageService just PutObjects into Storage:S3:Bucket and
never creates it), so the first upload failed with NoSuchBucket. Add a
minio-init one-shot (mc mb local/fsh) and gate api on its completion. No
anonymous policy -- objects are served via the API / presigned URLs.

docker-compose.yml (root/dev): had no migrator, so the API ran against an
empty schema (the DB is not migrated at API startup). Add a migrator
(apply --seed) + matching JWT/seed env so the seeded admin can log in,
gate api on it, and add a header clarifying this is the dev compose
(Aspire and deploy/docker are the other paths). DEV-ONLY secrets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@
Valkey 8 (BSD-3, the Linux Foundation Redis fork) replaces the
source-available Redis image across Aspire and both compose files. Drop-in:
the kit uses only StackExchange.Redis over RESP (cache, data protection,
SignalR backplane, quota) with no Redis Stack modules, and Hangfire is on
Postgres. Resource/service name stays "redis" so connection strings and
config keys do not churn.

- AppHost: AddRedis(...).WithImage("valkey/valkey","8") + .WithRedisInsight()
  (auto-wired cache browser; SSPL but dev-only). The valkey image ships
  redis-* symlinks and supports Aspire's TLS command, so the Redis
  integration works unmodified.
- docker-compose.yml (dev): valkey/valkey:8-alpine.
- deploy/docker (prod): valkey/valkey:8-alpine + valkey-server/valkey-cli.

Verified by booting Aspire: Valkey starts clean (tcp+tls listeners),
authenticated PING returns PONG, RedisInsight comes up auto-connected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… Valkey

Switch the Testcontainers image from redis:7-alpine to valkey/valkey:8-alpine
so the suite proves the cache round-trip works on the new engine. Verified
locally: 4 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add golden rule #10 — a user-facing change (feature, endpoint, config, infra,
breaking change) isn't done until the separate docs repo
(github.com/fullstackhero/docs) is updated to match and a changelog entry is
added. Also fix the stale "Redis 6379" ports line to "Valkey 6379".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(web): return 400 (not 500) for malformed requests / missing required params

Anonymous, tenant-scoped endpoints (forgot-password, reset-password,
self-register) bind a required `tenant` header. When it is missing,
ASP.NET Core throws BadHttpRequestException (StatusCode 400) during
parameter binding. GlobalExceptionHandler did not recognise that type
and rendered it as a generic 500.

Map BadHttpRequestException to its own StatusCode so missing required
header/route/query params (and unreadable/oversized bodies) surface as a
proper 400 (or 413, etc.) with a ProblemDetails body. The fix applies to
every endpoint with a required bound parameter, not just identity.

Mirror the mapping in the test-only DetailedTestExceptionHandler, add
integration regression tests for the three identity endpoints, and add
GlobalExceptionHandler unit tests.

Closes #1245


@

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit-driven fixes across the tenant dashboard. No backend or
BuildingBlocks changes; all verified (tsc clean, eslint 22->12 warnings,
build OK, Playwright 121/121).

Functional bugs:
- realtime-context: wire 4 subscribed-but-unregistered hub events
  (PresenceChanged, ChatMessagePinned/Unpinned, ChatChannelMemberRead) so
  live presence/pin/read-receipt updates actually arrive.
- chat-search: drop a stateful /g RegExp.test() whose lastIndex drifted
  and mis-highlighted matches.
- user-detail: key the staged-roles reset on userId (not the data array)
  so a background refetch can no longer wipe unsaved edits.

Accessibility:
- Combobox: replace invalid role="option" in a menu / role="combobox"
  without aria-expanded with valid menuitemradio; lift the nested
  interactive clear button out to a sibling.
- accessible names for reaction chips, quick-react emoji, EntitySearch,
  collapsed sidebar nav items.
- role="status"/sr-only on skeletons, role="progressbar" on upload bars,
  DialogTitle on the mobile nav Sheet, anchor-as-menuitem for link items,
  h1->h2 heading hierarchy, status rows hidden from the chat log region.
- raise placeholder contrast to AA (drop sub-AA alpha multipliers).

UX/correctness:
- retire hardcoded text-white/bg-white/bg-black via a new
  --color-overlay-foreground/--color-overlay token + semantic foregrounds.
- profile form: surface load errors, gate inputs while loading, seed once.
- wire the previously no-op "Reduce motion" toggle through the theme
  provider; fix the notifications "open bell" selector.

Cleanup:
- remove 3 `void X` dead-import hacks + a dead effect, swap a full-page
  window.location reload for router navigate(), and resolve all 10
  exhaustive-deps lint warnings via useMemo.

Tests: update 3 identity empty-state heading-level assertions to match the
corrected h2 hierarchy.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* @
chore(docker): remove redundant root docker-compose.yml

Local dev is covered by Aspire (FSH.Starter.AppHost) and production by
deploy/docker/docker-compose.yml. The root quick-run compose duplicated
infra config with no unique role; nothing references it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@

* fix(admin): UX/a11y/perf/correctness pass

Audit-driven fixes across the operator console. No backend or
BuildingBlocks changes; all verified (tsc clean, eslint 2 errors -> 0,
build OK, Playwright 93/93).

Build-red + real bugs:
- fix 2 lint errors: drop the unused `grant` prop on RowActions
  (active-grants-card) and the dangling `react/no-danger` disable
  directive that referenced an uninstalled rule (security).
- App.tsx: wrap RouterProvider in a top-level Suspense so the public
  lazy routes (login, password reset, confirm-email) have a boundary on
  cold chunk fetch instead of throwing.

Accessibility (admin had no eslint-plugin-jsx-a11y — now added):
- Field primitive now threads aria-describedby + aria-invalid to its
  control, so every RHF form announces hints/errors (one fix, all forms).
- notification-bell: drop the focusable aria-hidden click-away
  (tabIndex=-1), remove the invalid role="menu", add Escape-to-close,
  and stop redefining a component inside render.
- skip-to-content link + <main id> landmark in AppShell.
- accessible names on unlabelled search/filter inputs (users, audits x3,
  impersonate) and the icon-only webhook delete button.
- Segmented toggle gets role="group" + aria-pressed; impersonation
  details disclosure gets aria-expanded.
- role="alert" on login/users/tenants inline errors; role="status" on
  loaders; reduced-motion now also stops Tailwind's animate-spin.

Performance:
- impersonation list: collapse two overlapping 5s take:200 polls into one
  fetch + client-side filtering/counts.
- notification-bell: coalesce the per-event invalidation burst.
- security: lazy-import the ~50KB qrcode lib only at 2FA enrollment.

Correctness:
- sessions revoke: track in-flight ids in a Set so concurrent revokes
  don't clear each other's busy state (user-sessions-card + settings).
- invoices row: real <button> inside the <li> instead of a
  noninteractive <li role="button">.
- mobile-nav: capture the trigger node in the effect (ref-in-cleanup).

Tooling: add eslint-plugin-jsx-a11y (recommended) with no-autofocus off
and label-has-associated-control depth:3; lint passes clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* @
ci: split backend/frontend pipelines, pin SDK, dedupe test load

Replace the monolithic ci.yml with path-scoped backend.yml + frontend.yml so
a client-only change never builds/tests the API and vice versa.

- backend.yml: unit + integration each run ONCE with coverage collection; the
  coverage job now merges those reports instead of re-running the whole solution
  (the old double-run was the bulk of the load). Vuln scan gates on direct
  vulnerable packages. Drops the fragile bin/obj artifact hand-off.
- frontend.yml: lint + tsc/vite build + Playwright E2E, matrixed over admin and
  dashboard (Node 22, npm-cached) — the frontend had no CI before.
- global.json pins the .NET 10 GA SDK; all workflows use global-json-file and
  drop dotnet-quality: preview. Excluded from the template so scaffolded
  consumer projects are unaffected.
- Always-running "Backend CI" / "Frontend CI" gate jobs report green when their
  side is skipped, so required status checks resolve on cross-cutting PRs.

NOTE: branch protection must require the new "Backend CI"/"Frontend CI" checks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@iammukeshm iammukeshm marked this pull request as ready for review May 27, 2026 06:39
@iammukeshm iammukeshm merged commit a082612 into main May 27, 2026
32 checks passed
@iammukeshm iammukeshm deleted the develop branch May 27, 2026 06:59
@iammukeshm iammukeshm restored the develop branch May 27, 2026 07:01
@iammukeshm iammukeshm deleted the develop branch May 27, 2026 07:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants